Building essentia.js from source

You can find pre-compiled builds of essentia.js here.

Easiest way

  • Pull official essentia-emscripten docker image.

    docker pull mtgupf/essentia-emscripten:latest
    
  • Build essentia.js inside the docker container

    docker run --rm -v `pwd`:/essentia/ mtgupf/essentia-emscripten:latest /essentia/build-libs.sh
    

OR

Step-by-step

  • Run the following command to install all the Node.js dependencies.

    npm install 
    

You need to have a local installation of Node.js (v12.9.1 or later)

Generate essentia-wasm backend and bindings.

In order to build the associated essentia.js builds we need to compile the essentia C++ library to WebAssembly target using Emscripten.

In order to make the process easier, you could use the docker image (recommended) or build everything from the source on your local system.

(Optional)

We use some python scripts to automatically generate the C++ wrappers and the JS bindings from the Essentia upstream documentation using the Essentia python bindings. You need to have a local installation of Python with the required dependencies to re-generate the JS bindings and typescript wrapper source code.

pip install -r src/python/requirements.txt

Using docker

You need to have a local installation of docker.

  • Pull the official essentia-emscripten docker image.

    docker pull mtgupf/essentia-emscripten:latest
    
  • Clone or download the essentia.js repo.

  • cd to repo and mount the current directory as volume and run build-bindings.sh inside the docker container and check the new builds at the dist/ directory.

    docker run --rm -v `pwd`:/srv/workspace/ \
                    mtgupf/essentia-emscripten:latest \
                    /srv/workspace/build-bindings.sh \
                    Makefile.essentiajs
    

OR

Building from source locally

  • Install emscripten.

  • Clone the essentia repository.

    git clone https://github.com/MTG/essentia.git
    
  • Install the required 3rd party dependencies for essentia. Check the instructions here.

  • Compile essentia C++ library with the emscripten compiler. Check essentia documentation for more details.

    # configure build settings for essentia using kissfft
    emconfigure sh -c './waf configure --prefix=$EMSCRIPTEN/system/local/ --build-static --lightweight= --fft=KISS --emscripten'
    
    # compile and build essentia
    emmake ./waf
    
    # (you might need sudo rights)
    emmake ./waf install
    
  • Clone or download the essentia.js repo.

  • Finally, cd to repo and build the essentia.js bindings using one of the following commands. Check the new builds at the dist/ directory.

    Spawn a subshell inside the emscripten emconfigure in order to properlly access the emscripten variables.

    emconfigure sh -c './build-bindings.sh Makefile.essentiajs'
    

    Note: make you added the emscripten env variables to your bash profile.

Build essentia.js API and dist files

Build the final essentia.js JS API dist by running the following command.

npm run build-js-api

Once all the above-mentioned steps are done successfuly, you will find the final build essentia in the dist folder.

Build documentation

Build essentia.js API documentation using the following the command.

npm run build-api-docs

You can find the documentation files in the out directory once it's done.

Customizing your essentia.js builds

You can make customised builds of essentia.js for only a set of selected essentia standard algoithms using the code_generator.py python script which creates cpp source code and bindings for all the algos listed in included_algos.md. This list of included algorithms can be customised using the configure_bindings.py script.

cd src/python

# configure default list of algorithms for creating the js bindings
python configure_bindings.py 

# OR
# specify a list of algorithms for which you need to create the js bindings
python configure_bindings.py -i "['Key', 'HPCP']"

# OR
# you can specify the algorithm list by a text file with name of
# algorithms seperated by a new line.
python configure_bindings.py -i your_included_algos_list.txt

# for more cli options
python configure_bindings.py -h

Advanced

Writing custom essentia C++ extractor and cross-compile to JS for better performance on JS

You could also write your own customised essentia feature extractor in C++ and cross-compile to WASM using emscripten and our toolchain following the build instructions.

A custom C++ feature extractors can be write and cross-compiled to JS using our given template and examples in cases where the performance of feature extractors are significant. The essentia_custom_extractor.h, essentia_custom_extractor.cpp and bindings_extractor.cpp files demonstrates an example of writing a custom C++ extractor which can be cross-compiled and run on JS for both real-time and offline feature extraction cases. In this particular example the extractor computes Log-scaled MelSpectrogram for a given audio channel data input from the WebAudio API or any other sources. The extractor provide a object-oriented interface which can be used to configure, compute, reset and shutdown the algorithms as the user needs (relevant for better performance in batch or real-time feature extractors).

Usage
  • Build the custom cpp extractor using the given Makefile. Make sure you have the required dependencies for building Essentia WASM.
make
  • Usage on JavaScript would be like
// import essentia-wasm backend
import { EssentiaWASM } from 'essentia-custom-extractor.module.js';

// create an instance of our custom 'LogMelSpectrogramExtractor' 
// by passing our configuration settings for the given parameters
const extractor = new EssentiaWASM.LogMelSpectrogramExtractor(1024, // frameSize
                                                              512, // hopSize 
                                                              96, // numBands
                                                              'hann'); // windowType


// Use the Web Audio API to decode the audio channel data from an url of a audio file
const audioURL = "https://freesound.org/data/previews/328/328857_230356-lq.mp3";
const audioContext = new AudioContext();
const response = await fetch(audioURL);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
// getChannelData of channel number 0 (since it's a mono audio signal)
const audioData = audioBuffer.getChannelData(0);

// Now, let's compute the log-mel spectrogram feature for the given audio input
let melSpectrogram =  extractor.compute(audioData);

// reset the internal states of the algorithms used in the extractor
extractor.reset();

// reconfigure our extractor with new parmeter settings
extractor.configure(1024, 512, 128, 'hann');
// compute with new settings
let melSpectrogram =  extractor.compute(audioData);

// delete algorithms and free memory after it's use
extractor.shutdown();
extractor.delete();